module uart #(
  parameter CLK_DIV = 0 // 1..2048; default value is deliberately invalid
)(
  input i_wb_clk,
  input i_wb_rst,
  input i_wb_cyc,
  input i_wb_stb,
  // not needed
  //input i_wb_adr,
  input i_wb_we,
  input [3:0] i_wb_sel,
  input [31:0] i_wb_dat,
  output reg o_wb_ack,
  output reg [31:0] o_wb_dat,

  output reg o_txd
);

//  0  idle
//  1  start bit on o_txd
//  2  sending_data[0] on o_txd
//     ...
//  9  sending_data[7] on o_txd
// 10  stop bit on o_txd

reg [10:0] state;

localparam S_IDLE  =  0;
localparam S_START =  1;
localparam S_DATA0 =  2;
localparam S_DATA1 =  3;
localparam S_DATA2 =  4;
localparam S_DATA3 =  5;
localparam S_DATA4 =  6;
localparam S_DATA5 =  7;
localparam S_DATA6 =  8;
localparam S_DATA7 =  9;
localparam S_STOP  = 10;

reg [7:0] sending_data, pending_data;
reg data_pending;

reg [10:0] cnt;

always @(posedge i_wb_clk) begin
  if (i_wb_rst) begin
    o_wb_ack <= 1'b0;
    o_txd <= 1'b1;
    state <= 2**S_IDLE;
    data_pending <= 1'b0;
  end else begin
    o_wb_ack <= i_wb_cyc & i_wb_stb & ~o_wb_ack;
    if (i_wb_cyc & i_wb_stb) begin
      if (i_wb_we) begin
        if (i_wb_sel[0]) begin
          if (~data_pending) begin
            data_pending <= 1'b1;
            pending_data <= i_wb_dat[7:0];
          end
        end
      end else begin
        o_wb_dat <= {8'b0, 8'b0, 7'b0, data_pending, 8'b0};
      end
    end
    // put sending_data onto wire
    case (1'b1)
      state[S_IDLE]: begin
        if (data_pending) begin
          data_pending <= 1'b0;
          sending_data <= pending_data;
          o_txd <= 1'b0;
          cnt <= CLK_DIV-1;
          state <= 2**S_START;
        end
      end
      state[S_START]: begin
        if (|cnt) begin
          cnt <= cnt-1;
        end else begin
          cnt <= CLK_DIV-1;
          {sending_data[6:0],o_txd} <= sending_data;
          state <= 2**S_DATA0;
        end
      end
      state[S_DATA0]: begin
        if (|cnt) begin
          cnt <= cnt-1;
        end else begin
          cnt <= CLK_DIV-1;
          {sending_data[6:0],o_txd} <= sending_data;
          state <= 2**S_DATA1;
        end
      end
      state[S_DATA1]: begin
        if (|cnt) begin
          cnt <= cnt-1;
        end else begin
          cnt <= CLK_DIV-1;
          {sending_data[6:0],o_txd} <= sending_data;
          state <= 2**S_DATA2;
        end
      end
      state[S_DATA2]: begin
        if (|cnt) begin
          cnt <= cnt-1;
        end else begin
          cnt <= CLK_DIV-1;
          {sending_data[6:0],o_txd} <= sending_data;
          state <= 2**S_DATA3;
        end
      end
      state[S_DATA3]: begin
        if (|cnt) begin
          cnt <= cnt-1;
        end else begin
          cnt <= CLK_DIV-1;
          {sending_data[6:0],o_txd} <= sending_data;
          state <= 2**S_DATA4;
        end
      end
      state[S_DATA4]: begin
        if (|cnt) begin
          cnt <= cnt-1;
        end else begin
          cnt <= CLK_DIV-1;
          {sending_data[6:0],o_txd} <= sending_data;
          state <= 2**S_DATA5;
        end
      end
      state[S_DATA5]: begin
        if (|cnt) begin
          cnt <= cnt-1;
        end else begin
          cnt <= CLK_DIV-1;
          {sending_data[6:0],o_txd} <= sending_data;
          state <= 2**S_DATA6;
        end
      end
      state[S_DATA6]: begin
        if (|cnt) begin
          cnt <= cnt-1;
        end else begin
          cnt <= CLK_DIV-1;
          {sending_data[6:0],o_txd} <= sending_data;
          state <= 2**S_DATA7;
        end
      end
      state[S_DATA7]: begin
        if (|cnt) begin
          cnt <= cnt-1;
        end else begin
          cnt <= CLK_DIV-1;
          o_txd <= 1'b1;
          state <= 2**S_STOP;
        end
      end
      state[S_STOP]: begin
        if (|cnt) begin
          cnt <= cnt-1;
        end else begin
          if (data_pending) begin
            data_pending <= 1'b0;
            sending_data <= pending_data;
            o_txd <= 1'b0;
            cnt <= CLK_DIV-1;
            state <= 2**S_START;
          end else begin
            state <= 2**S_IDLE;
          end
        end
      end
    endcase
  end
end

endmodule
